/*******************************************************************************
 * @note Copyright (C) 2011-2022, Shanghai Panchip Microelectronics Co., Ltd. 
 * @SPDX-License-Identifier: Apache-2.0
 * @history - V0.1, 2022-09-28
*******************************************************************************/
#include "hardware.h"
#include "pan3029.h" 
#include "radio.h"
#include "spi.h"
#include "at.h"
#include "user.h"
#include "service_pt.h"
#include "service_nodemode.h"
#include "aes_ecb.h"

pt_proc_t pt_proc;

extern node_config_t node_config;
extern rf_config_t pn3029_config;
extern uint8_t pan3029_irq_trigged_flag;

/**
 * @brief handling the event of SETB
 * @param null 
 * @return null
 */
void pt_io_set_evt(void)
{
    /* if pt process is not initialized, suspend the process. */
    if (pt_proc.status != PT_STATUS_INITIALIZED)
    {
        return;
    }

    if ( GPIO_SETB_LEVEL_GET() == LEVEL_ACTIVE )
    {
        pt_proc.evt |= PT_EVT_SETB_ACTIVE;
    }
    else
    {
        pt_proc.evt |= PT_EVT_SETB_INACTIVE;
    }
}

/**
 * @brief handling the event of uart
 * @param null 
 * @return null
 */
void pt_uart_set_rx_evt(void)
{
    if ( pt_proc.status != PT_STATUS_INITIALIZED )
    {
        return;
    }
    else if ( node_config.work_mode != WORKMODE_PT )
    {
        return;
    }
    else if ( fifo_is_empty(&uart_rx_fifo) )
    {
        return;
    }
    
    /* if setb is high that means node working in active mode  */
    if ( GPIO_SETB_LEVEL_GET() )
    {
        pt_proc.evt |= PT_EVT_UART_RX_DONE;
    }
    else
    {
        /* clear uart rx fifo  */
        fifo_flush(&uart_rx_fifo);
    }
}

/**
 * @brief handling the event of rf(pan3029)
 * @param null 
 * @return null
 */
void pt_rf_set_evt(u32 evt)
{
    if ( pt_proc.status != PT_STATUS_INITIALIZED )
    {
        return;
    }
    else if ( node_config.work_mode != WORKMODE_PT )
    {
        return;
    }

    if(evt&PT_EVT_RF_RX_DONE)
    {
        int len;
        u8 *buf;
        pt_msg_t *msg = &pt_proc.rf_msg;
        pt_aes_t *aes = &pt_proc.aes_msg;
        
        if ( node_config.pt_class == PT_CLASS_PT )
        {
            buf = pt_proc.rf_msg.buf;
        }
        else if ( node_config.pt_class == PT_CLASS_P2P )
        {
            buf = &pt_proc.rf_msg.addr0;
        }
        else
        {
            return;
        }
        
        if ( node_config.aes_enable )
        {
            len = PAN3029_recv_packet((u8*)&aes->len);
            if ( len < 17 || len >= PT_BUF_SIZE ) /* aes length must bigger than 17 */
            {
                return;
            }
            else if ( ((len-1)&AES_BYTE_ALIGNMENT) != 0 )
            {
                return;
            }

            if ( aes->len <= 0|| aes->len >= PT_BUF_SIZE )
            {
                return;
            }
            
            pt_debug("aes len:%d", aes->len);
            
            static uint8_t pPlain[PT_BUF_SIZE]__attribute__ ((aligned(4))) = {0};
            u8 key[16]__attribute__ ((aligned (4)));
            
            memcpy(key, node_config.aes_key, 16);

            int ret = decrypt(&aes->buf[0], pPlain, key, 16, len-1);
            if ( ret != 0 )
            {
                pc_debug("aes buf is not 4bytes align.");
                return;
            }
            
            memcpy(buf, pPlain, aes->len);
            
            len = aes->len;
        }
        else
        {
            len = PAN3029_recv_packet((u8*)buf);
            if ( len <= 0 || len >= PT_BUF_SIZE  )
            {
                return;
            }
        }

        if ( node_config.pt_class == PT_CLASS_P2P )
        {
            if ( len <= 2 )
            {
                /* len must bigger than 2 in p2p mode */
                return;
            }

            /* broadcast case */
            if ( node_config.addr0 == 255 )
            {
                if ( pt_proc.rf_msg.addr0 != 255 )
                {
                    return;
                }
            }
            else /* point2point case */
            {
                if ( node_config.addr0 != pt_proc.rf_msg.addr0
                     || node_config.addr1 != pt_proc.rf_msg.addr1)
                {
                    return;
                }
            }
            
            msg->len = len - 2;
            msg->flag = TRUE;
        }
        else
        {
            msg->len = len;
            msg->flag = TRUE;
        }
    }

    pt_proc.evt |= evt;
}

/**
 * @brief rf receiving timeout callback
 * @param null 
 * @return null
 */
void pt_rf_rx_timeout_cb(void *ptr)
{
    pt_proc.evt |= PT_EVT_RF_RX_TIMEOUT;
}

/**
 * @brief rf sending timeout callback
 * @param null 
 * @return null
 */
void pt_rf_tx_timeout_cb(void *ptr)
{
    pt_proc.evt |= PT_EVT_RF_TX_TIMEOUT;
}

/**
 * @brief the interrupt process of pan3029
 * @param null 
 * @return null
 */
void pt_rf_irq_process( void )
{
    if( pan3029_irq_trigged_flag )
    {
        uint8_t irq;
        pan3029_irq_trigged_flag = false;
        irq = PAN3029_get_irq();
        
        pt_debug("irq:%x",irq);

        if(irq & REG_IRQ_RX_DONE)
        {
            pt_rf_set_evt(PT_EVT_RF_RX_DONE);
            PAN3029_clr_irq();
        }
        if(irq & REG_IRQ_CRC_ERR)
        {
            PAN3029_clr_irq();
            pt_rf_set_evt(PT_EVT_RF_CRC_ERR);
        }
        if(irq & REG_IRQ_RX_TIMEOUT)
        {
            PAN3029_clr_irq();
            pt_rf_set_evt(PT_EVT_RF_RX_TIMEOUT);
        }
        if(irq & REG_IRQ_TX_DONE)
        {
            PAN3029_clr_irq();
            pt_rf_set_evt(PT_EVT_RF_TX_DONE);
        }
    }
}

/**
 * @brief handle the received uart datas
 * @param null 
 * @return null
 */
int pt_uart_recv_handle(void)
{
    int ret;
    pt_msg_t *p = &pt_proc.uart_msg;
    
    p->len = uart_fifo_read(p->buf, PT_BUF_SIZE);
    if ( aux_mode == AUX_MODE_EMPTY )
    {
        if ( fifo_is_empty(&uart_rx_fifo) )
        {
            aux_level_set(AUX_FIFO, LEVEL_INACTIVE);
        }
        else
        {
            aux_level_set(AUX_FIFO, LEVEL_ACTIVE);
        }
    }
    else //AUX_MODE_FULL
    {
        aux_level_set(AUX_FIFO, LEVEL_INACTIVE);
    }

    /* BEGIN: check +++ cmd */
    if(pt_proc.pt_at_switch == PT_SWITCH_STATE_IDLE)
    {
        if ( p->len == 3 )
        {
            p->buf[3]=0;
            if ( strstr((char *)p->buf, "+++") )
            {
                /* get a "+++" cmd */
#ifdef DDL_UART_DMA_YES
                hw_uart0_dma_send("a", 1);
#else
                uart0_sendBuf("a", 1);
#endif

                /* switch to ack state */
                pt_proc.pt_at_switch = PT_SWITCH_STATE_WACK;
                return -1;
            }
        }
    }
    else if (pt_proc.pt_at_switch == PT_SWITCH_STATE_WACK)
    {
        if(p->len==1&&p->buf[0]=='a')
        {
#ifdef DDL_UART_DMA_YES
            hw_uart0_dma_send("OK\r\n", 4);
#else
            uart0_sendBuf("OK\r\n", 4);
#endif

            /* get a "a" cmd, then switch to at server mode */
            nm_enter_at_mode();
            nm_set_at_op();
            pt_proc.pt_at_switch = PT_SWITCH_STATE_IDLE;
            return -1;
        }
    }
    /* END: check +++ cmd */
    
    pt_proc.pt_at_switch = PT_SWITCH_STATE_IDLE;
    
    pt_proc.evt &= ~PT_EVT_RF_TX_DONE;

    u8 *buf;
    int len;

    if ( node_config.aes_enable )
    {
        pt_aes_t *aes = &pt_proc.aes_msg;

        /* aes length must be aligned by 128bit */
        len = app_aes_get_align_len(p->len);
        
        u8 key[16] __attribute__ ((aligned (4)));
        memcpy(key, node_config.aes_key, 16);
        
		int ret = encrypt(p->buf, aes->buf, key, 16, len);
        if ( ret != 0 )
        {
            pc_debug("aes buf is not 4bytes align.");
            return -1;
        }
        
        aes->len = (u8)p->len; /* length of plain datas */
        len = len+1;           /* add sizeof(len) */
        buf = &aes->len;       /* payload include len and encrypt datas */ 
    }
    else
    {
        buf = p->buf;
        len = p->len;
    }
    
    ret = rf_enter_continous_tx();
    if(ret != OK)
    {
        pt_debug("rf_enter_continous_rx err");
        return -1;
    }
    
    /* push datas to rf tx fifo */
    ret = rf_continous_tx_send_data(buf, len);
    if ( ret != Ok )
    {
        pt_debug("push fail");
        rf_enter_continous_rx();
        return -1;
    }
    
    print_hex(p->buf, p->len);

    /* calculate one package time(ms) */
    clock_time_t t = (clock_time_t)get_package_time(&pn3029_config, len); 
    t += 1000;
    stimer_stop(&pt_proc.stimer_rf_rx);
    stimer_set(&pt_proc.stimer_rf_tx, t, \
               STIMER_MODE_ONESHOT, \
               pt_rf_tx_timeout_cb, NULL);

    return 0;
}

/**
 * @brief check what to do next when run in pt
 * @param null 
 * @return null
 */
void pt_check_do_next(void)
{
    /* check something have not done? */
    /* if node is in pass through mode */
    if ( GPIO_SETB_LEVEL_GET() == LEVEL_ACTIVE
         && pt_proc.evt&PT_EVT_UART_RX_DONE )
    {
        pt_debug("");
        
        /* jump to idle state to do what hasn't been done yet */
        pt_proc.state = PT_STATE_IDLE;
        return;
    }
    /* if node is in timed wakeup mode */
    else if ( GPIO_SETB_LEVEL_GET() == LEVEL_INACTIVE
              && node_config.wakeup_time > 0
              && pt_proc.evt&PT_EVT_RF_RX_DONE )
    {
        pt_debug("");
        
        /* jump to idle state to do what hasn't been done yet */
        pt_proc.state = PT_STATE_IDLE;
        return;
    }

    /* clear the pt event */
    pt_proc.evt = PT_EVT_NONE;
    pt_proc.rf_msg.flag = FALSE;
    uart_rx_fifo_flush();
    //GPIO_AUX_LEVEL_SET_L;
    aux_level_set(AUX_FIFO, LEVEL_INACTIVE);

    /* check next what to do */
    /* if node is in data pass through mode */
    if ( GPIO_SETB_LEVEL_GET() == LEVEL_ACTIVE )
    {
        /* enable uart tx&rx */
        Uart_EnableFunc(M0P_UART0, UartRenFunc);
        
        /* start stimer for rf receiving timeout */
        stimer_stop(&pt_proc.stimer_rf_tx);
        stimer_stop(&pt_proc.stimer_rf_rx);
		pt_debug("");

        /* rf continous rx */
        rf_enter_continous_rx();

        /* jump to idle state */
        pt_proc.state = PT_STATE_IDLE;
    }
    else /*if node is in timed wake-up mode or sleep mode */
    {
        /* disable uart tx&rx function */
        Uart_DisableFunc(M0P_UART0, UartRenFunc);
        
        /* wakeup mode */
        if (node_config.wakeup_time)
        {
            pt_proc.rtc_on = TRUE;
			
//			rf_set_cad(CAD_DETECT_THRESHOLD_20,CAD_DETECT_NUMBER_3);
//            /* let rf enter rx state to check cad signal */
//            int ret = rf_enter_continous_rx();
//            if ( ret == Ok )
//            {
                /* aux is low when node exit sleep state. */
                //GPIO_AUX_LEVEL_SET_L;
				u32 syml = getOnepreamble(pn3029_config.bw,pn3029_config.sf);//bw,sf
				/* if cad signal is active */
				if ( check_cad_rx_inactive(syml) == LEVEL_ACTIVE )
				{
					/* start stimer for rf receiving timeout */
					stimer_stop(&pt_proc.stimer_rf_tx);
					stimer_set(&pt_proc.stimer_rf_rx,      \
							   node_config.rf_rx_timeout,   \
							   STIMER_MODE_ONESHOT,       \
							   pt_rf_rx_timeout_cb, NULL);
					
					/* jump to rf recv state */
					pt_proc.state = PT_STATE_RF_RX;
				}
				else/* if cad signal is inactive */
				{
					/* jump to rf sleep state */
					pt_proc.state = PT_STATE_SLEEP;
				}
//            }
//            else /* check cad fail */
//            {
//                /* jump to rf sleep state */
//                pt_proc.state = PT_STATE_SLEEP;
//            }
        }
        else /* sleep mode */
        {
            pt_proc.rtc_on = FALSE;
 
            /* jump to rf sleep state */
            pt_proc.state = PT_STATE_SLEEP;
        }
    }
}

/**
 * @brief node enter sleep when run in pt
 * @param null 
 * @return null
 */
void pt_enter_sleep(int rtc_enable)
{
    aux_level_set(AUX_SLEEP, LEVEL_ACTIVE);
    pt_debug("enter sleep");
    pt_proc.evt = PT_EVT_NONE;
	rf_deepsleep();
    stimer_stop(&pt_proc.stimer_rf_tx);
    stimer_stop(&pt_proc.stimer_rf_rx);
    hw_rtc_cfg(rtc_enable, node_config.wakeup_time/1000);
    mcu_enter_lp();
}

/**
 * @brief node exit sleep when run in pt
 * @param null 
 * @return null
 */
void pt_exit_sleep(void)
{
    hw_rtc_cfg(FALSE, node_config.wakeup_time/1000);
    mcu_exit_lp();
    pt_debug("exit sleep");
    aux_level_set(AUX_SLEEP, LEVEL_INACTIVE);
}

/**
 * @brief passthrough process
 * @param null 
 * @return null
 */
void pt_process(void)
{
    int ret;

    /* if pt process is not initialized, suspend the process. */
    if (pt_proc.status != PT_STATUS_INITIALIZED)
    {
        return;
    }
    
    /* rf irq process */
    pt_rf_irq_process();
    
    switch ( pt_proc.state )
    {
        case PT_STATE_INIT :
            ret = rf_init();
            if(ret != OK)
            {
                pt_debug("rf init err");
                break;
            }
            else
            {
                pt_debug("rf init ok");
            }
            
            /* set rf params */
            user_set_rf_para(&pn3029_config);
            
            /* check what next to be done */
            pt_check_do_next();
            break;
        case PT_STATE_IDLE:
        case PT_STATE_RF_RX:
            if ( pt_proc.evt&PT_EVT_SETB_ACTIVE )
            {
                pt_debug("PT_EVT_SETB_ACTIVE");
                pt_proc.evt &= ~PT_EVT_SETB_ACTIVE;
                
            }
            else if( pt_proc.evt&PT_EVT_SETB_INACTIVE )
            {
                pt_debug("PT_EVT_SETB_INACTIVE");
                pt_proc.evt &= ~PT_EVT_SETB_INACTIVE;
                pt_check_do_next();
            }
            else if ( pt_proc.evt&PT_EVT_UART_RX_DONE )
            {
                pt_debug("PT_EVT_UART_RX_DONE");
                pt_proc.evt &= ~PT_EVT_UART_RX_DONE;

                /* if setb is active, send the received datas through the rf port */
                if ( GPIO_SETB_LEVEL_GET() == LEVEL_ACTIVE )
                {
                    /* pop data from uart rxfifo and push them to rf fifo */
                    if(0 == pt_uart_recv_handle())
                    {
                        /* now, uart rx fifo is empty, should set aux low */
                        //GPIO_AUX_LEVEL_SET_L;
                        //aux_level_set(AUX_FIFO, LEVEL_INACTIVE);

                        pt_proc.state = PT_STATE_RF_TX;
                    }
                }
                else /* if setb is inactive, discard the recv datas */
                {
                    uart_rx_fifo_flush();
                    aux_level_set(AUX_FIFO, LEVEL_INACTIVE);
                    
                    /* check next work and do it */
                    pt_check_do_next();
                }
            }
            else if ( pt_proc.evt&PT_EVT_RF_RX_DONE )
            {
                if ( GPIO_SETB_LEVEL_GET() == LEVEL_ACTIVE )
                {
                    /* check pin aux is busy or not */
                    if ( GPIO_AUX_LEVEL_GET() == LEVEL_INACTIVE )
                    {
                        pt_debug("PT_EVT_RF_RX_DONE");
                        pt_proc.evt &= ~PT_EVT_RF_RX_DONE;
                        
                        /*send the rf received datas through the uart port*/
                        aux_level_set(AUX_DATA, LEVEL_ACTIVE);
                        
                    #ifdef DDL_UART_DMA_YES
                        hw_uart0_dma_send(pt_proc.rf_msg.buf, pt_proc.rf_msg.len);
                    #else
                        uart0_sendBuf(pt_proc.rf_msg.buf, pt_proc.rf_msg.len);
                    #endif
                    
                        //GPIO_AUX_LEVEL_SET_L;
                        aux_level_set(AUX_DATA, LEVEL_INACTIVE);
                        
                        stimer_stop(&pt_proc.stimer_rf_rx);

                        /* check next work and do it */
                        pt_check_do_next();
                    }
                    else
                    {
                        /* node maybe is receiving datas from uart.
                         * nothing to do here.just wait for uart receving over.
                         */
                    }
                }
                else
                {
                    pt_debug("PT_EVT_RF_RX_DONE");
                    pt_proc.evt &= ~PT_EVT_RF_RX_DONE;
                    
                    aux_level_set(AUX_DATA, LEVEL_ACTIVE);
                    delay1ms(node_config.aux_head_time);//aux head delay time
                    /*send the rf recv datas through the uart port*/
                    uart0_sendBuf(pt_proc.rf_msg.buf, pt_proc.rf_msg.len);
                    delay1ms(node_config.aux_tail_time);//aux tail delay time
                    //GPIO_AUX_LEVEL_SET_L;
                    aux_level_set(AUX_DATA, LEVEL_INACTIVE);
                    
                    stimer_stop(&pt_proc.stimer_rf_rx);

                    /* check next work and do it */
                    pt_check_do_next();
                }
            }
            else if ( pt_proc.evt&PT_EVT_RF_RX_TIMEOUT )
            {
                pt_debug("EVT_RECV_TIMEOUT");
                pt_proc.evt &= ~PT_EVT_RF_RX_TIMEOUT;
                
                /* check next work and do it */
                pt_check_do_next();
            }
            else if ( pt_proc.evt&PT_EVT_RF_CRC_ERR )
            {
                pt_debug("PT_EVT_RF_CRC_ERR");
                pt_proc.evt &= ~PT_EVT_RF_CRC_ERR;
                
                /* check next work and do it */
                pt_check_do_next();
            }
            break;
        case PT_STATE_RF_TX:
            if(pt_proc.evt&PT_EVT_RF_TX_DONE)
            {
                pt_proc.evt &= ~PT_EVT_RF_TX_DONE;
                stimer_stop(&pt_proc.stimer_rf_tx);
                pt_debug("tx pkgs success[len:%d]", pt_proc.uart_msg.len);

                /* check next work and do it */
                pt_check_do_next();
            }
            else if ( pt_proc.evt&PT_EVT_RF_TX_TIMEOUT )
            {
                pt_proc.evt &= ~PT_EVT_RF_TX_TIMEOUT;
                
                stimer_stop(&pt_proc.stimer_rf_tx);
                pt_debug("PT_EVT_RF_TX_TIMEOUT");
                
                /* check next work and do it */
                pt_check_do_next();
            }
            break;
        case PT_STATE_UART_TX :
            pt_proc.state = PT_STATE_INIT;
            break;
        case PT_STATE_SLEEP :
            /* enter sleep state */
            pt_enter_sleep(pt_proc.rtc_on);
            
            /* enter exit state */
            pt_exit_sleep();
    
            /* after wakeup, init rf chip firstly. */
            pt_proc.state = PT_STATE_INIT;
            break;
        default:
            /* after wakeup, init rf chip firstly. */
            pt_proc.state = PT_STATE_INIT;
            break;
    }
}

/**
 * @brief delete passthrough process
 * @param null 
 * @return null
 */
void pt_delete(void)
{
    pt_proc.status = PT_STATUS_UNINITIALIZED;
    pt_proc.count = 0;
    pt_proc.state = PT_STATE_INIT;
    pt_proc.evt = PT_EVT_NONE;

    stimer_stop(&pt_proc.stimer_rf_tx);
    stimer_stop(&pt_proc.stimer_rf_rx);

    rf_deepsleep();
}

/**
 * @brief passthrough process initialize
 * @param null 
 * @return null
 */
void pt_init(void)
{
    pt_proc.count = 0;
    pt_proc.state = PT_STATE_INIT;
    pt_proc.evt = PT_EVT_NONE;
    pt_proc.status = PT_STATUS_INITIALIZED;
    
    uart_rx_fifo_flush();

    rf_init();
    pt_debug("pt process init");
}

